package com.squirrel.common.domain;

import com.squirrel.common.param.danmu.SendParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@ServerEndpoint(value = "/ws/{videoId}/{userId}", encoders = {ServerEncoder.class})
@Component
@Slf4j
public class WebSocketServer {

    /**
     * 这是要做什么？？？
     * 初始化，依赖注入后执行
     *
     * @author 关远键
     * @date 2021/11/16 13:12
     */
    @PostConstruct
    public void init() {
        log.info("Initializing WebSocketServer");
    }

//    private volatile static WebSocketServer instance;

    // 视频ID -> 在线人数
    private static final ConcurrentHashMap<Integer, AtomicInteger> onlineMap = new ConcurrentHashMap<>();
    // 视频ID -> 信息
    private static final ConcurrentHashMap<Integer, Set<WebSocketServer>> videoMap = new ConcurrentHashMap<>();

    private Session session;
    private Integer videoId;
    private Integer userId;

//    private WebSocketServer() {
//        // 反射可以破坏单例
//    }
//
//    public WebSocketServer getInstance() {
//        if (null == instance) {
//            synchronized (WebSocketServer.class) {
//                if (null == instance) {
//                    instance = new WebSocketServer();
//                }
//            }
//        }
//
//        return instance;
//    }


    /**
     * 建立连接时调用
     *
     * @param session 会话信息
     * @param videoId 视频ID
     * @param userId  用户ID
     * @author 关远键
     * @date 2021/11/18 19:16
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("videoId") Integer videoId, @PathParam("userId") Integer userId) {
        log.info("建立连接时调用");
        this.session = session;
        this.videoId = videoId;
        this.userId = userId;
        if (!videoMap.containsKey(videoId)) {
            HashSet<WebSocketServer> set = new HashSet<>();
            set.add(this);
            videoMap.put(videoId, set);
            onlineMap.put(videoId, new AtomicInteger(0));
        } else {
            videoMap.get(videoId).add(this);
        }

        addOnlineCount(videoId);
        sendAllMessage(videoId, "有"+ getOnlineCount(videoId) +"人正在观看" );    //通知所有正在观看的用户

        log.info("用户【{}】进入了直播间{}，当前在线人数为：{}", userId, videoId,getOnlineCount(videoId) );
    }

    /**
     * 连接关闭时调用，移除用户
     *
     * @author 关远键
     * @date 2021/11/18 19:17
     */
    @OnClose
    public void onClose() {
        if (videoMap.containsKey(videoId)) {
            videoMap.get(videoId).remove(this);
            subOnlineCount(videoId);
        }
        sendAllMessage(videoId, "有"+ getOnlineCount(videoId) +"人正在观看" );
        log.info("用户【{}】退出了房间{}，当前在线人数为：{}", userId, videoId, getOnlineCount(videoId));
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param session
     * @param message
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("房间：{},用户：【{}】，消息内容：{}", videoId, userId, message);
        sendAllMessage(videoId, message);
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     * @author 关远键
     * @date 2021/11/18 19:39
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误：{}，Session ID： {}" + error.getMessage() + "----" + session.getId());
        error.printStackTrace();
    }


    /**
     * 发送消息
     *
     * @param message
     * @author 关远键
     * @date 2021/11/18 19:39
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        System.out.println(message);
    }

    /**
     * 发送弹幕
     *
     * @param sendParam
     * @author 关远键
     * @date 2021/11/18 19:39
     */
    public void sendDanmu(SendParam sendParam) throws IOException, EncodeException {
        this.session.getBasicRemote().sendObject(sendParam);
        System.out.println(sendParam);
    }


    /**
     * 给同一个直播间的观众发送消息
     *
     * @param message 弹幕
     * @author 关远键
     * @date 2021/11/18 10:22
     */
    public void sendAllMessage(Integer videoId, String message) {
        if (videoMap.containsKey(videoId)) {
            new Thread(() -> {
                Set<WebSocketServer> set = videoMap.get(videoId);
                for (WebSocketServer server : set) {
                    try {
                        // 不发给自己
//                        log.info("发送者：{}，接收者：{}", WebSocketServer.this.userId, server.userId);
//                        if (Objects.equals(server.userId, WebSocketServer.this.userId)) {
//                            continue;
//                        }
                        // 全都发送
                        server.sendMessage(message);
                    } catch (Exception e) {
                        log.error("发送消息失败");
                    }
                }
            }).start();
        }
    }

    /**
     * 给同一个直播间的观众发送弹幕
     *
     * @param sendParam 弹幕
     * @author 关远键
     * @date 2021/11/18 10:22
     */
    public void sendAllDanmu(Integer videoId, SendParam sendParam) {
        if (videoMap.containsKey(videoId)) {
            new Thread(() -> {
                Set<WebSocketServer> set = videoMap.get(videoId);
                for (WebSocketServer server : set) {
                    log.info("发送成功:{}", server.userId.equals(WebSocketServer.this.userId));
                    log.info("发送：{}，接收:{}", sendParam.getAuthor(), server.userId);
                    if (server.userId.equals(sendParam.getAuthor())) {
                        continue;
                    }
                    try {
                        server.sendDanmu(sendParam);
                    } catch (IOException | EncodeException e) {
                        log.error("发送弹幕失败");
                        log.error(e.getMessage());
                    }
                }
            }).start();
        }
    }

    /**
     * 获取当前在线人数
     *
     * @param videoId 视频ID
     * @return java.util.concurrent.atomic.AtomicInteger
     * @author 关远键
     * @date 2021/11/18 16:57
     */
    public static synchronized AtomicInteger getOnlineCount(Integer videoId) {
        return onlineMap.get(videoId);
    }

    /**
     * 当前观看人数+1
     *
     * @param videoId 视频ID
     * @author 关远键
     * @date 2021/11/18 16:57
     */
    public static synchronized void addOnlineCount(Integer videoId) {
        AtomicInteger atomicInteger = onlineMap.get(videoId);
        atomicInteger.incrementAndGet();
        onlineMap.put(videoId, atomicInteger);

    }

    /**
     * 当前观看人数-1
     *
     * @param videoId 视频ID
     * @author 关远键
     * @date 2021/11/18 16:58
     */
    public static synchronized void subOnlineCount(Integer videoId) {
        AtomicInteger atomicInteger = onlineMap.get(videoId);
        atomicInteger.decrementAndGet();
        onlineMap.put(videoId, atomicInteger);
    }

//    public static boolean sendInfo(@PathParam("videoId") Integer videoId,
//                                   @PathParam("userId") String userId,
//                                   String message) {
//        // 校验token合法性
//        sendAllMessage(videoId, message);
//        return true;
//    }


    /**
     * 发送消息，实践表明，每次浏览器刷新，session会发生变化。
     *
     * @param session
     * @param message
     */
//    public static void SendMessage(Session session, String message) {
//        try {
//            session.getBasicRemote().sendText(String.format("%s (From Server，Session ID=%s)", message, session.getId()));
//        } catch (IOException e) {
//            System.out.println("发送消息出错：{}" + e.getMessage());
//            e.printStackTrace();
//        }
//    }

    /**
     * 群发消息
     *
     * @param message
     * @throws IOException
     */
//    public static void BroadCastInfo(String message) throws IOException {
//        for (Session session : SessionSet) {
//            if (session.isOpen()) {
//                SendMessage(session, message);
//            }
//        }
//    }

    /**
     * 指定Session发送消息
     *
     * @param sessionId
     * @param message
     * @throws IOException
     */
//    public static void SendMessage(String message, String sessionId) throws IOException {
//        Session session = null;
//        for (Session s : SessionSet) {
//            if (s.getId().equals(sessionId)) {
//                session = s;
//                break;
//            }
//        }
//        if (session != null) {
//            SendMessage(session, message);
//        } else {
//            System.out.println("没有找到你指定ID的会话：{}" + sessionId);
//        }
//    }

}